package game;

import fields.*;

import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

import dataAccess.*;

public class Controller {
	
	Cup cup;
	Gameboard board;
	BoundaryGUI out;
	ArrayList<Player> players;
	ArrayList<Ownable> owned, pawnedField;
	ArrayList<Territory> houseFields;
	List<LuckyCard> luckycard;
	int playerfield, numberOfDoubles, JailcardsUsed;
	Start startField;
	GameOptions gameoption;
	
	public Controller() {
		cup = new Cup();
		board = new Gameboard();
		out = new BoundaryGUI();
		players = new ArrayList<Player>();
		startField = (Start) board.getField(1);
		gameoption = new GameOptions();
		owned = new ArrayList<Ownable>();
		pawnedField = new ArrayList<Ownable>();
		houseFields = new ArrayList<Territory>();
		luckycard = new ArrayList<LuckyCard>();
		JailcardsUsed = 0;
	}
	
	public void playGame()
	{
		
		try {
			new Connector();
		} catch (InstantiationException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		out.startGUI();
		if (out.welcomeText()) {
			try{
				gameoption.NewGame(players);
			} catch(DALException e){
				System.out.println(e.getMessage()); // �ndre denne!!
			}
		}
		else{
			try {
				players = gameoption.LoadPlayer(board);
				gameoption.LoadField(board);
				board.setLuckyCardStack(gameoption.LoadLuckyCards());
			} catch (DALException e) {
				// TODO Auto-generated catch block
				System.out.println(e.getMessage()); //�ndre denne!!
			}
		}
		
		int i=0;
		while (true)
		{
				playerTurn(players.get(i));
				
			if (players.get(i).hasLost())
			{
				lostPlayer(players.get(i));
				players.remove(i);
			}
			
			if (cup.rolledDoubles() && numberOfDoubles<3 && !players.get(i).isInJail()) {
				out.showMessage("You rolled doubles. You get another turn.");
				numberOfDoubles++;
			} else if (numberOfDoubles==3 && !players.get(i).isInJail()) {
				out.showMessage("You rolled doubles three times in a row. You have been jailed.");
				out.removeCar(players.get(i).getField(), players.get(i).getName());
				players.get(i).setInJail();
				out.setCar(players.get(i).getField(), players.get(i).getName());
				numberOfDoubles=0;
			} else {
				numberOfDoubles=0;
				i++;
				if (i>=players.size()) {
					luckycard = board.getLuckyCardList();
					try {
						gameoption.saveJailCards(JailcardsUsed);
						gameoption.saveGame(players, owned, pawnedField, houseFields, luckycard);
					} catch (DALException e) {
						e.printStackTrace();
					}
					out.showMessage("The game has been saved.");
					JailcardsUsed = 0;
					i=0;
				}
				}
			
			if (players.size()==1) break;
		}
		
		out.playerHasWon(players.get(0).getName());
		//Sletter Spillet i Databasen.
		try {
			GameDB.EndGame();
		} catch (DALException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		out.closeGUI();
	}
		
	private void playerTurn(Player player) {
		if (player.isInJail()) {
			if (player.getTurnsInJail()==0) {
				String jailChoice = out.haveToGetOutOfJail(player.outOfJailCards());
				if (jailChoice.equals("Pay 1000")) {
					player.withdraw(1000);
					out.removeCar(player.getField(), player.getName());
					player.getOutOfJail();
					out.setCar(player.getField(), player.getName());
				} else {
					out.removeCar(player.getField(), player.getName());
					player.useOutOfJailCard();
					out.setCar(playerfield, player.getName());
					board.putOutOfJailCardBack();
				}
			} else {
				String jailChoice = out.playerInJail(player.outOfJailCards());
				if (jailChoice.equals("Use Luckycard")) {
					out.removeCar(player.getField(), player.getName());
					player.useOutOfJailCard();
					out.setCar(playerfield, player.getName());
					board.putOutOfJailCardBack();
					out.setCar(player.getField(), player.getName());
					JailcardsUsed++;
				}
				else if (jailChoice.equals("Pay 1000")) {
					player.withdraw(1000);
					out.removeCar(player.getField(), player.getName());
					player.getOutOfJail();
					out.setCar(player.getField(), player.getName());
				} else {
					out.showMessage("Roll doubles to get out of jail!");
					cup.rollDices();
					out.setDices(cup.getArray());
					if (cup.rolledDoubles()) {
						out.removeCar(player.getField(), player.getName());
						player.getOutOfJail();
						player.move(cup.getRoll());
						playerfield=player.getField();
						out.setCar(playerfield, player.getName());
					} else player.stayInJail();
				}
			}
		} else {
			while (true) {
				String choice = out.turnText(player.getName(), player.totalHouses(), canBuyProperty(player), player.canBuild(), player.propertiesToPawn(), player.pawnedProperties());
				if (choice.equals("Roll dices")) break;
				else if (choice.equals("Buy elses property")) {
					//Spilleren vil handle.
					int buy = out.getFieldnumber("Enter number of field you want to buy:");
					if (!(board.getField(buy) instanceof Ownable)) out.showMessage("This field is not ownable");
					else if (isOwnable(board.getField(buy))) out.showMessage("This field is not yet owned. You have to land on it.");
					else if (player.isOwner(board.getField(buy))) out.showMessage("You are already the owner of this field!");
					else {
						if (board.getField(buy) instanceof Territory) {
							if (((Territory) board.getField(buy)).getHouses()>0) 
								out.showMessage("You can't pawn this property, while there is buildings on it.");
							else {
								Ownable field = (Ownable) board.getField(buy);
								out.showMessage("This field is owned by "+field.getOwnerName()+".");
								int bid = out.getUserBid();
								if (out.acceptBid(bid)) {
									out.showMessage(player.getName()+" is now the owner of "+field.getName()+".");
									field.getOwner().makePropertyDeal(field, player, bid);
									out.setOwner(field.getNumber(), field.getOwner().getName());
									owned.add((Ownable) board.getField(buy));
								} else {
									out.showMessage(field.getOwnerName()+" rejects your offer.");
								}
							}
						} else {
							Ownable field = (Ownable) board.getField(buy);
							out.showMessage("This field is owned by "+field.getOwnerName()+".");
							int bid = out.getUserBid();
							if (out.acceptBid(bid)) {
								out.showMessage(player.getName()+" is now the owner of "+field.getName()+".");
								field.getOwner().makePropertyDeal(field, player, bid);
								out.setOwner(field.getNumber(), field.getOwner().getName());
								owned.add((Ownable) board.getField(buy));
							} else {
								out.showMessage(field.getOwnerName()+" rejects your offer.");
							}
						}
					}
				} else if (choice.equals("Build house")) {
					//Spilleren vil bygge hus.
					int build = out.getFieldnumber("Enter number of field where you want to build:");
					if (!(board.getField(build) instanceof Territory)) out.showMessage("It isn't possible to build anything here.");
					else if (!player.isOwner(board.getField(build))) out.showMessage("You are not the owner of this field.");
					else if (!player.canBuildAtProperty(build, ((Territory) board.getField(build)).getColor())) out.showMessage("You don't own all the streets.");
					else if (player.needToEvenlyBuild(build)) out.showMessage("You need to build on the other properties first.");
					else {
						int houses = ((Territory) board.getField(build)).getHouses();
						if (houses==4) {
							if (out.yesOrNo("Build hotel at "+board.getField(build).getName(), "Yes", "No")) {
								player.addBuilding(build);
								out.addHotel(build);
								houseFields.add((Territory) board.getField(build));
							}
						} else {
							if (out.yesOrNo("Build a house at "+board.getField(build).getName()+"?", "Yes", "No")) {
								player.addBuilding(build);
								out.setHouses(build, ++houses);
								houseFields.add((Territory) board.getField(build));
							}
						}
					}
				} else if (choice.equals("Unpawn")) {
					//Spilleren vil afpantsætte en grund.
					int unpawn = out.getFieldnumber("Enter number of field you want to unpawn:");
					if (!(board.getField(unpawn) instanceof Ownable)) out.showMessage("This field is not ownable. You can't unpawn it.");
					else if (!(player.isOwner(board.getField(unpawn)))) out.showMessage("You are not the owner of this field.");
					else if (!(((Ownable) board.getField(unpawn)).isPawned())) out.showMessage("This field is not pawned");
					else {
						((Ownable) board.getField(unpawn)).unpawn();
						out.showMessage(board.getField(unpawn).getName()+" is now unpawned.");
					}
				} else if (choice.equals("Sell house")) {
					//Spilleren vil sælge sine huse.
					sellBuildings(player);
				} else if (choice.equals("Pawn")) {
					//Spilleren vil pantsætte sine grunde.
					pawnProperty(player);
				}
				updateAccounts();
			}
			cup.rollDices();
			out.setDices(cup.getArray());
			out.removeCar(player.getField(), player.getName());
			int beforeRoll = player.getField();
			player.move(cup.getRoll());
			playerfield=player.getField();
			out.setCar(playerfield, player.getName());
			out.landOnFieldText(board.getField(playerfield).getName());
			if (board.getField(playerfield).getNumber()==5) {
				//Hvis spilleren lander på indkomstskatten.
				if (out.yesOrNo("Do you want to pay 4000 or 10% of your total assets?", "4000", "10 %")) {
					out.showMessage("You payed 4000 in taxes");
					player.setChoice(true);
					board.getField(playerfield).landOnField(player);
				} else {
					int tax = player.calculateTotalAssets()/10;
					out.showMessage("You payed "+tax+" in taxes.");
					board.getField(playerfield).landOnField(player);
				}
			} else {
				out.showMessage(board.getField(playerfield).fieldInfo(player));
				if (board.getField(playerfield) instanceof TryYourLuck) {
					out.removeCar(player.getField(), player.getName());
					board.getField(playerfield).landOnField(player);
					out.setCar(player.getField(), player.getName());
				} else {
					if (board.getField(playerfield) instanceof Brewery) {
						player.setDice(cup.getRoll());
					}
					if (board.getField(playerfield) instanceof Ownable) {
						if (isOwnable(board.getField(playerfield))) {
							if (out.yesOrNo("Do you want to buy this property", "Yes", "No")) {
								player.setChoice(true);
								out.setOwner(board.getField(player.getField()).getNumber(), player.getName());
								owned.add((Ownable) board.getField(playerfield));
							}
						}
					}
					board.getField(playerfield).landOnField(player);
					updateAccounts();
				}
				if (playerfield!=player.getField()) {
					player.setChoice(false);
					board.getField(player.getField()).landOnField(player);
				}
				
				if (startField.passesStart(beforeRoll, player.getField())) {
					out.showMessage("You passed start. You get 4000.");
					player.deposit(4000);
				}
				if (player.isOutOfCash() && (player.totalProperties()>0 || player.totalHouses()>0)) {
					//Spilleren er nødt til at sælge huse eller pantsætte.
					out.setBalance(player.getName(), player.getAccount());
					out.setBalance(player.getName(), player.getAccount());
					out.showMessage("You are broke. You have to get cash, to stay in the game!");
					while (true) {
						String brokeChoice = out.brokeOptions(player.totalHouses(), true);
						if(brokeChoice.equals("Nothing")) break;
						else if (brokeChoice.equals("Pawn")) {
							pawnProperty(player);
						}
						else if (brokeChoice.equals("Sell building")) {
							//The player wants to sell building.
							sellBuildings(player);
						}
					}
					
				}
			}
			}
		updateAccounts(); //Alle spilleres kontoer opdateres.
	}
	
	private void updateAccounts() {
		for (int i=0; i<players.size(); i++)
			out.setBalance(players.get(i).getName(), players.get(i).getAccount());
	}
	
	private boolean isOwnable(Field field) {
		for (int i=0; i<players.size(); i++) {
			if (players.get(i).isOwner(field)) return false;
		}
		return true;
	}
	
	private boolean canBuyProperty(Player player) {
		int owned=0;
		for (int i=1; i<41; i++) {
			if (board.getField(i) instanceof Ownable) {
				if (!isOwnable(board.getField(i))) owned++;
			}
		}
		return player.totalProperties()<owned;
	}
	
	private void sellBuildings(Player player) {
		int remove = out.getFieldnumber("Enter number of field where you want to remove a building:");
		if (!(board.getField(remove) instanceof Territory)) out.showMessage("It isn't possible to build anything here.");
		else if (!player.isOwner(board.getField(remove))) out.showMessage("You are not the owner of this field.");
		else if (((Territory) board.getField(remove)).getHouses()==0) out.showMessage("There isn't any buildings on this property.");
		else if (player.needToEvenlyRemove(remove)) out.showMessage("You have to remove houses on the other properties first.");
		else {
			int houses = ((Territory) board.getField(remove)).getHouses();
			player.removeBuilding(remove);
			out.setHouses(remove, --houses);
			houseFields.add((Territory) board.getField(remove));
		}
	}
	
	private void pawnProperty(Player player) {
		int pawned = out.getFieldnumber("Enter the number of the field you want to pawn:");
		if (!(board.getField(pawned) instanceof Ownable)) out.showMessage("This field is not ownable");
		else if (!player.isOwner(board.getField(pawned))) out.showMessage("You are not the owner of this field");
		else if (board.getField(pawned) instanceof Territory) {
			if (((Territory) board.getField(pawned)).getHouses()>0) {
				out.showMessage("You can't pawn this property, while there is buildings on it.");
			} else {
				out.showMessage(board.getField(pawned).getName()+" is now pawned.");
				((Ownable) board.getField(pawned)).pawn();
				pawnedField.add((Ownable) board.getField(pawned));
				out.setBalance(player.getName(), player.getAccount());
			}
		}
		else if (board.getField(pawned) instanceof Ownable){
			out.showMessage(board.getField(pawned).getName()+" is now pawned.");
			((Ownable) board.getField(pawned)).pawn();
			pawnedField.add((Ownable) board.getField(pawned));
			out.setBalance(player.getName(), player.getAccount());
		}
	}
	
	private void lostPlayer(Player player) {
		out.playerHasLost(player.getName(), player.getField());
		for (int i = 0; i<player.totalProperties(); i++) {
			if (player.getProperty(i) instanceof Territory)
				out.setHouses(player.getProperty(i).getNumber(), 0);
			out.removeOwner(player.getProperty(i).getNumber());
		}
		player.bankrupt();
		player.DeleteFromDB();
	}
}

